Skip to content

fix: repopulate natural-key lookup table on projection rebuild (#259)#261

Merged
jeremydmiller merged 1 commit into
mainfrom
feat/259-natural-key-rebuild
Jun 27, 2026
Merged

fix: repopulate natural-key lookup table on projection rebuild (#259)#261
jeremydmiller merged 1 commit into
mainfrom
feat/259-natural-key-rebuild

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Closes #259. Parity with marten#4788 / marten#4793.

Bug

NaturalKeyProjection maintains the pc_natural_key_X lookup table via the inline-append path, which drives off newly-appended StreamActions. The async-daemon rebuild replays already-persisted events without appending streams, so ApplyAsync never fired during a rebuild — the table stayed empty afterward and FetchForWriting/FetchLatest by natural key missed.

Fix (two hook points, mirroring the Marten fix)

  1. TeardownProjectionStateAsync — when the projection source carries a NaturalKeyDefinition, also wipe pc_natural_key_X (rides the same DELETE FROM loop as the doc tables) so the rebuild starts from a clean slate.
  2. StartProjectionBatchAsync in ShardExecutionMode.Rebuild — look up the source by range.ShardName.Name; when it has a NaturalKeyDefinition, feed range.Events per tenant through the new NaturalKeyProjection.QueueUpsertsForEvents. Operations are routed through batch.SessionForTenant(tenantId) so they flush in the same batch transaction as the rebuilt snapshots.

NaturalKeyProjection was refactored so ApplyAsync (StreamAction-driven) and QueueUpsertsForEvents (raw-IEvent-driven, pulling stream id/key + tenant off the event) share one per-event operation builder.

Archived-stream delta

The shared builder keeps the Archived-event branch. Polecat's rebuild event loader filters is_archived = 0, so archived streams' events are not replayed and their natural-key rows are not re-emitted on rebuild — correct, since an archived stream shouldn't resolve as an active natural-key lookup. Covered by a dedicated test.

Tests (Bug_259_natural_key_rebuild)

  • single-stream: append → DELETE FROM pc_natural_key_* → rebuild → table has 1 row and FetchForWriting by natural key succeeds (the exact repro from the issue);
  • multi-stream: 5 streams repopulated on rebuild;
  • teardown + archived: an active stream's row is re-emitted while an archived stream's row is not (proves teardown wiped the table, since rebuild only inserts).

Regression sweep (natural-key + Daemon + Projections): 218 passed / 0 failed.

🤖 Generated with Claude Code

Parity with marten#4788/#4793. NaturalKeyProjection maintains pc_natural_key_X
via the inline-append path (driven by newly-appended StreamActions). The async
daemon rebuild replays persisted events without appending streams, so the table
was never repopulated on rebuild and FetchForWriting/FetchLatest by natural key
missed after a rebuild.

Two hook points (mirroring the Marten fix):
- TeardownProjectionStateAsync: when the source carries a NaturalKeyDefinition,
  also wipe pc_natural_key_X so the rebuild starts clean.
- StartProjectionBatchAsync (Rebuild mode): look up the source by shard name and,
  when it has a NaturalKeyDefinition, feed range.Events per tenant through the new
  NaturalKeyProjection.QueueUpsertsForEvents — routed via batch.SessionForTenant so
  the upserts flush in the same batch transaction as the rebuilt snapshots.

NaturalKeyProjection refactored to share one per-event operation builder between
ApplyAsync (StreamAction-driven) and QueueUpsertsForEvents (IEvent-driven, pulling
stream id/key + tenant off the event). The shared builder keeps the Archived-event
branch; archived streams are correctly excluded from rebuild (the loader filters
is_archived = 0), so their natural-key rows are not re-emitted.

Tests: table repopulated on single- and multi-stream rebuild; FetchForWriting by
natural key works after rebuild; teardown wipes the table and archived streams are
excluded from the rebuilt lookup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jeremydmiller
jeremydmiller merged commit deb37d5 into main Jun 27, 2026
7 checks passed
@jeremydmiller
jeremydmiller deleted the feat/259-natural-key-rebuild branch June 27, 2026 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Natural-key lookup table not repopulated on projection rebuild (parity with marten#4788)

1 participant